#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <signal.h>
#include "ptylib.h"

sig_atomic_t sigcaught = 0;

void sig_term(int sig)
{
	sigcaught = 1;
}

int loop(int fd, int ignoreeof)
{
	char buf[BUFSIZ];
	pid_t pid = fork();
	if (pid == 0) {
		for (;;) {
			int len = read(STDIN_FILENO, buf, sizeof(buf));
			if (len < 0) {
				exit(0);
			}
			if (len == 0) {
				break;
			}
			write(fd, buf, len);
		}
		if (ignoreeof == 0) {
			kill(getppid(), SIGTERM);
		}
		exit(0);
	}

	signal(SIGTERM, sig_term);
	for (;;) {
		int len = read(fd, buf, sizeof(buf));
		if (len <= 0) {
			break;
		}
		write(STDOUT_FILENO, buf, len);
	}
	if (sigcaught) {
		kill(pid, SIGTERM);
	}
	return 0;
}

const char OPTSTR[] = "d:einv";
int main(int argc, char **argv)
{
	int opt, noecho = 0, ignoreeof = 0;
	int interactive = isatty(STDIN_FILENO);
	char *driver = NULL;
	while ((opt = getopt(argc, argv, OPTSTR)) != -1) {
		switch (opt) {
		case 'd':
			driver = optarg;
			break;
		case 'e': //noecho for slave pty
			noecho = 1;
			break;
		case 'i': //ignore standard input
			ignoreeof = 1;
			break;
		case 'n':
			interactive = 0;
			break;
		default:
			fprintf(stderr, "unknow option\n");
			exit(0);
		}
	}

	int mfd = 0;
	pid_t pid = 0;
	if (interactive) {
		struct termios term;
		tcgetattr(STDIN_FILENO, &term);
		struct winsize wsize;
		ioctl(STDIN_FILENO, TIOCGWINSZ, &wsize);
		pid = pty_fork(&mfd, &term, &wsize);
	} else {
		pid = pty_fork(&mfd, NULL, NULL);
	}

	if (pid == 0) {
		if (noecho) {
			set_noecho(STDIN_FILENO);
		}
		if (execvp(argv[optind], &argv[optind]) < 0) {
			exit(0);
		}
	}

	if (interactive && driver == NULL) {
		printf("entry raw mode\n");
		tty_raw(STDIN_FILENO);
		atexit(tty_reset);
	}

	if (driver) {
		do_driver(driver);
	}

	loop(mfd, ignoreeof);
	return 0;
}